import Queue
import threading
from copy import copy
from FetchOnThread import FetchOnThread
from LoopingThread import LoopingThread
from BruTile.Web.RequestTms import RequestTms
import time

'''
# <summary>
# Provides methods to fetch tiles for any thread.
# </summary>
'''
class TileFetcher(object):

    __looping_thread = None
    __tile_schema = None
    __extent = None
    __current_resolution = 0.0
    __max_nr_threads = 5
    __numberOfThreads = 0
    __memory_cache = None
    __tiles_in_process = {}
    __lock = threading.Lock()
    
    request_queue = Queue.Queue()
    
    fetchingTiles = None
    doAfterFetch = None
    updateLayerInfo = None
    updateLayer = None
    caching_tiles = False
    #request_builder = None;#RequestTms("http://a.tile.openstreetmap.org", "png")
    #temp_extents = None
    
    i = 0
    o = 0
    n = 0
    a = 0
    
    __extent_lock = threading.Lock()
    __tiles_extent = {}
    
    QuitThread = None
    
    def __init__(self):
        '''Constructor'''
    
    '''Constructor for MainThread'''
    def InitMainThread(self, memory_cache, updateLayer, tile_schema, url):
        self.__request_builder = copy(RequestTms(url, "png"))
        self.__memory_cache = copy(memory_cache)
        self.__tile_schema = copy(tile_schema)
        self.__extent = copy(tile_schema.extent)
        self.fetchingTiles = self.fetchTiles
        self.updateLayer = updateLayer
        self.doAfterFetch = self.addToCache
        self.__looping_thread = copy(LoopingThread(self.request_queue).start())
    
    '''Constructor for TileSeeds'''
    def InitSeedThread(self, tile_schema, url, QuitThread, UpdateLayerInfo):
        self.s=url
        self.__request_builder = copy(RequestTms(url, "png"))
        self.__tile_schema = copy(tile_schema)
        self.__extent = copy(tile_schema.extent)
        self.doAfterFetch = self.tileFetched
        self.QuitThread = QuitThread
        self.updateLayerInfo = UpdateLayerInfo
    
    def viewChanged(self, extent, resolution):
        if(self.__extent.equals(extent)) and (self.__current_resolution == resolution):
            return
        
        self.__extent = extent
        self.__resolution = resolution

        values = (self.__tile_schema, extent, self.__resolution, self.fetchingTiles, None)
        self.request_queue.put(values)
    
    '''Set amount of threads used for fetching tiles'''
    def setNrThreads(self, nr):
        self.__max_nr_threads = nr

    def stopFetchingThread(self):
        values = (None, None, None, None, self.QuitThread)
        self.request_queue.put(values)
    
    def fetchTiles(self, tiles, extent, resolution):
        for tile_info in tiles:
            self.fetchTile(tile_info)
    
    def fetchTile(self, tile_info):
        if (self.__memory_cache.containsKey(tile_info.key)):
            return
        
        self.fetchOnThread(tile_info)
        
    def fetchOnThread(self, tile_info):        
        while len(self.__tiles_in_process) > self.__max_nr_threads:
            time.sleep(0.1)

        self.__lock.acquire()
        try:
            if not(self.__tiles_in_process.has_key(tile_info.key)):
                que = Queue.Queue()
                url = self.__request_builder.getUri(tile_info.key)
                que.put((self.doAfterFetch, url, tile_info))
                self.__tiles_in_process[tile_info.key] = tile_info
                fetch_on_thread = FetchOnThread(tile_info.key, que).start()
                self.__numberOfThreads += 1
        finally:
            self.__lock.release()
    
    def TilesInProcess(self, threadname):
        if (self.__numberOfThreads > 0):
            return True
        else:
            return False
    
    '''Method called by TileSeeds after tile is fetched'''
    def tileFetched(self, tile_info, bitmap):
        self.tileDoneProcessing(tile_info)
        self.updateLayerInfo(tile_info, (bitmap is None))
    
    '''Method called by Main thread after tile is fetched'''    
    def addToCache(self, tile_info, bitmap):
        if not(bitmap is None):
            if not(self.caching_tiles):
                self.__memory_cache.add(tile_info.key, bitmap)
                self.updateLayer(tile_info)
        self.tileDoneProcessing(tile_info)
    
    def tileDoneProcessing(self, tile_info):
        self.__lock.acquire()
        try:
            del(self.__tiles_in_process[tile_info.key])
            self.__numberOfThreads -=1
        finally:
            self.__lock.release()